package cn.jimmie.learn.viewArt.ball;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Scroller;

/**
 * function : 滑动相关的操作
 *
 * <p> Create Time: 2019年01月01日 10:15 </p>
 * <p> @author Jimmie.Qian  </p>
 */
class AssistScrollerRunnable extends AnimatorListenerAdapter implements Runnable,
    ValueAnimator.AnimatorUpdateListener {
    private final static int DURATION = 100;
    private final static int AUTO_HIDE_TIME = 3 * 1000;
    // 事件源
    private final EventSource mSource;
    // 圆滑滑动辅助类
    private final Scroller mScroller;
    // 贴边停滞状态下的动画更新
    private final ValueAnimator mAnimator;
    // 悬浮球的方向
    private int mLocation = AssistBallView.LEFT;
    private int lastX, lastY;

    // 是否处于 移动到边缘的状态
    // true 表示 移动到边缘
    // false 表示 移动到隐藏状态
    private boolean isMTE;
    // 监听器是否有效
    private boolean listening;

    private OnScrollerListener mListener;

    AssistScrollerRunnable(EventSource source, OnScrollerListener listener) {
        this.mListener = listener;
        this.mScroller = new Scroller(source.getContext(),
            new AccelerateDecelerateInterpolator());
        this.mSource = source;
        this.mAnimator = ValueAnimator.ofInt(255, 128);
        this.mAnimator.setDuration(AUTO_HIDE_TIME);
        this.mAnimator.setInterpolator(new LinearInterpolator());
    }

    void removeAllListeners() {
        releaseResource();
        mAnimator.removeAllListeners();
        mAnimator.removeAllUpdateListeners();
        listening = false;
    }

    private void start(int dx, int dy, int duration) {
        if (!listening) {
            listening = true;
            mAnimator.addListener(this);
            mAnimator.addUpdateListener(this);
        }
        releaseResource();
        mScroller.startScroll(0, 0, dx, dy, duration);
        mSource.post(this);
        lastX = 0;
        lastY = 0;
        isMTE = true;
    }

    private void startDelay() {
        releaseResource();
        mAnimator.start();
    }

    void releaseResource() {
        mAnimator.cancel();
        mScroller.forceFinished(true);
        mSource.removeCallbacks(this);
    }

    void moveToEdge() {
        mSource.onHideChanged(false);
        int halfSize = mSource.getSize() / 2;
        int x = mSource.lx();
        int y = mSource.ly();
        int W = mSource.W();
        int H = mSource.H();
        int dx = 0;
        if (y + halfSize < H / 6) {
            // 向上贴
            mLocation = AssistBallView.TOP;
            if (x < 0) dx = -x;
            else if (x > W - halfSize * 2) dx = (W - halfSize * 2 - x);
            start(dx, -y, duration(y));
        } else if (y + halfSize > H * 5 / 6) {
            // 向下贴
            mLocation = AssistBallView.BOTTOM;
            if (x < 0) dx = -x;
            else if (x > W - halfSize * 2) dx = (W - halfSize * 2 - x);
            start(dx, H - 2 * halfSize - y, duration(H - y));
        } else if (x < W / 2) {
            // 向左贴
            mLocation = AssistBallView.LEFT;
            start(-x, 0, duration(x));
        } else {
            // 向右贴
            mLocation = AssistBallView.RIGHT;
            start(W - halfSize * 2 - x, 0, duration(W - x));
        }

    }

    @Override
    public void run() {
        if (mScroller.computeScrollOffset()) {
            int curX = mScroller.getCurrX();
            int curY = mScroller.getCurrY();
            mSource.moveBy(curX - lastX, curY - lastY);
            mSource.post(this);
            lastX = curX;
            lastY = curY;
        } else {
            mSource.removeCallbacks(this);
            if (isMTE) {
                startDelay();
                mListener.onAttachedEdge();
            }
            mSource.onScrollOver();
            if (!isMTE) mSource.onHideChanged(true);
        }
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mSource.onEdgeState((int) animation.getAnimatedValue());
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        mSource.onEdgeState(255);
        int halfSize = mSource.getSize() / 2;
        switch (mLocation) {
            case AssistBallView.LEFT:
                start(-halfSize, 0, DURATION);
                break;
            case AssistBallView.RIGHT:
                start(halfSize, 0, DURATION);
                break;
            case AssistBallView.TOP:
                start(0, -halfSize, DURATION);
                break;
            case AssistBallView.BOTTOM:
                start(0, halfSize, DURATION);
        }
        isMTE = false;
    }

    private int duration(int distance) {
        int duration = (int) (250f * (Math.abs(distance) * 1f / 800));
        return duration > DURATION ? duration : DURATION;
    }

    interface OnScrollerListener {
        void onAttachedEdge();
    }
}
